
#include "etl_string.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <algorithm>

using namespace etl;
using namespace std;

etl_string::etl_string()
{
}

etl_string::etl_string( const etl_string& obj )
{
    copy( obj.c_str() );
}

etl_string::etl_string( const char* s )
{
    copy( s );
}

etl_string::etl_string( long lv, int b )
{
    from_long( lv, b );
}

etl_string::etl_string( unsigned long ulv, int b )
{
    from_ulong( ulv, b );
}

etl_string::etl_string( double dv )
{
    from_double( dv );
}

etl_string::~etl_string()
{
}

const char* etl_string::c_str() const
{
    return internalString.c_str();
}

etl_string& etl_string::operator = ( const etl_string& obj )
{
    return copy( obj.c_str() );
}

bool etl_string::operator == ( const etl_string& obj )
{
    return (strcmp( this->c_str(), obj.c_str() ) == 0) ? true : false;
}

bool etl_string::operator == ( const char* s )
{
    return (strcmp( this->c_str(), s ) == 0) ? true : false;
}

bool etl_string::operator != ( const etl_string& obj )
{
    return (strcmp( this->c_str(), obj.c_str() ) != 0) ? true : false;
}

bool etl_string::ends_with( const char* s )
{
    int passed_len = strlen( s );
    int this_len = strlen( internalString.c_str() );

    if( this_len > passed_len )
    {
        if( strncmp( internalString.c_str() + (this_len - passed_len), s, passed_len ) == 0 )
            return true;
    }

    return false;
}

bool etl_string::begins_with( const char* s )
{
    int passed_len = strlen( s );
    int this_len = strlen( internalString.c_str() );

    if( this_len > passed_len )
    {
        if( strncmp( internalString.c_str(), s, passed_len ) == 0 )
            return true;
    }

    return false;
}

bool etl_string::contains( const char* obj )
{
    if( internalString.find( obj, 0 ) != string::npos )
        return true;
    return false;
}

etl_string& etl_string::operator = ( const char* new_string )
{
    return copy( new_string );
}

#ifdef WIN32
#define VSNPRINTF _vsnprintf
#else
#define VSNPRINTF vsnprintf
#endif

etl_string etl_string::format( char* f, ... )
{
    etl_string formatted;
    
    if( f )
    {
        va_list arg_list;
        va_start(arg_list, f);

        int chars_written = VSNPRINTF(NULL, 0, f, arg_list);

        int len = chars_written + 1;

        char str[len];

        chars_written = VSNPRINTF(str, len, f, arg_list);

        va_end(arg_list);

        formatted = str;
    }

    return formatted;
}

int etl_string::format_in_place(char* f, ...)
{                   
    if (f)
    {
        va_list arg_list;
        va_start(arg_list, f);

        int chars_written = VSNPRINTF(NULL, 0, f, arg_list);

        int len = chars_written + 1;

        char str[len];

        chars_written = VSNPRINTF(str, len, f, arg_list);

        va_end(arg_list);

        internalString = str;

        return 0;
    }

    return -1;
}

etl_string etl_string::operator + (const etl_string& obj)
{
    internalString.append( obj.internalString );
    return *this;
}

etl_string etl_string::operator + (const char* c_string)
{
    internalString.append( c_string );
    return *this;
}

etl_string& etl_string::operator += (const etl_string& obj)
{
    *this = *this + obj;
    return *this;
}

etl_string& etl_string::operator += (const char* obj)
{
    *this = *this + obj;
    return *this;
}

etl_string etl_string::sub_string( int index, int length )
{
    char new_string_buffer[length+1];
    new_string_buffer[length]=0;
    const char* from = c_str() + index;
    for (int i = 0; i < length; i++)
        new_string_buffer[i] = *from++;
    etl_string ret = new_string_buffer;
    return ret;
}

int etl_string::split(char delim, etl_list<etl_string>& parts)
{
    const char* src = c_str();
    int src_len = strlen(src);
    const char* begin = src;
    const char* end = src;

    while ((end < (src + src_len)))
    {
        while (*end != delim && (end < (src + src_len)))
            end++;
        int part_len = end - begin;
        etl_string part = sub_string((begin - src), part_len);
        parts.append(part);
        end += 1;
        begin = end;
    }

    return 0;
}

int etl_string::split(etl_string delim, etl_list<etl_string>& parts)
{
    const char* src = c_str();
    int src_len = strlen(src);
    int delim_len = strlen(delim.c_str());
    const char* begin = src;
    const char* end = src;

    while ((end < (src + src_len)))
    {
        while (strncmp(end, delim.c_str(), delim_len) != 0
               && (end < (src + src_len)))
            end++;
        int part_len = end - begin;
        etl_string part = sub_string((begin - src), part_len);
        parts.append(part);
        end += delim_len;
        begin = end;
    }

    return 0;
}

int etl_string::munch( int len )
{
    int original_len = strlen(c_str());
    int new_len = original_len - len;
    *this = sub_string(len, new_len);
    return 0;
}

int etl_string::remove(char toBeRemoved)
{
    etl_list<etl_string> temp;
    split(toBeRemoved, temp);
    etl_string tempString;
    temp.iter_begin();
    while( temp.iter_valid() )
    {
        tempString += *temp.iter_data();
        temp.iter_next();
    }

    *this = tempString;
    return 0;
}

int etl_string::cond_chomp( etl_string match )
{
    int srcLen = length();
    int matchLen = match.length();

    if( srcLen > matchLen )
    {
        const char* matchStr = match.c_str();
        const char* src = c_str() + srcLen - matchLen;
        if( memcmp( src, matchStr, matchLen ) == 0 )
        {
            internalString = std::string( c_str(), src-c_str() );
        }        
        return 0;
    }

    return -1;
}

unsigned long etl_string::length() const
{
    return internalString.length();
}

int etl_string::from_long(long lv,int base)
{
    if( base == 16 )
        return format_in_place((char*)"%lx", lv);
    else if( base == 10 )
        return format_in_place((char*)"%ld", lv);
    return -1;
}

int etl_string::from_ulong(unsigned long lv,int base)
{
    if( base == 16 )
        return format_in_place((char*)"%lx", lv);
    else if( base == 10 )
        return format_in_place((char*)"%lu", lv);
    return -1;
}

int etl_string::from_double(double dv)
{
    return format_in_place((char*)"%lf", dv);
}

long etl_string::to_long(int base)
{
    long lv = 0;
    if( base == 16 )
        sscanf(c_str(),(char*)"%lx", &lv);
    else if( base == 10 )
        sscanf(c_str(),(char*)"%ld", &lv);
    return lv;
}

unsigned long etl_string::to_ulong(int base)
{
    unsigned long ulv = 0;
    if( base == 16 )
        sscanf(c_str(), (char*)"%lx", &ulv);
    if( base == 10 )
        sscanf(c_str(), (char*)"%lu", &ulv);
    return ulv;
}

double etl_string::to_double()
{
    double dv = 0;
    sscanf(c_str(), (char*)"%lf", &dv);
    return dv;
}


int etl_string::to_lower()
{
    transform( internalString.begin(), internalString.end(), internalString.begin(), ::tolower );
    return 0;
}

int etl_string::to_upper()
{
    transform( internalString.begin(), internalString.end(), internalString.begin(), ::toupper );
    return 0;
}

etl_string etl_string::to_base64( unsigned char* data, long len )
{
    etl_string ret_string;

    if( data && len > 0 )
    {
        char* writer = NULL;
        char* out    = NULL;
        int   i      = 0;

        int encoded_len = (len * 4) / 3 + 4;

        writer = out = (char*)malloc( encoded_len );
        //Added by NSA 3-8-06 to fix erroneous data bug.
        memset(writer, 0, encoded_len);
        memset(out, 0, encoded_len);
        //End Add
        out[ encoded_len - 1 ] = 0;

        for( i = 0; i < len; i += 3 )
        {
            unsigned char b1=0, b2=0, b3=0, b4=0, b5=0, b6=0, b7=0;

            b1 = data[i];

            if( i + 1 < len )
                b2 = data[ i+1 ];

            if( i + 2 < len )
                b3 = data[ i + 2 ];

            b4 = b1 >> 2;
            b5 = ((b1&0x3)<<4) | (b2>>4);
            b6 = ((b2&0xf)<<2) | (b3>>6);
            b7 = b3 & 0x3f;

            *writer++ = encode_byte( b4 );
            *writer++ = encode_byte( b5 );

            if( i + 1 < len )
                *writer++ = encode_byte( b6 );
            else
                *writer++ = '=';


            if( i + 2 < len )
                *writer++ = encode_byte( b7 );
            else
                *writer++ = '=';
        }

        ret_string = out;

        free( out );
    }

    return ret_string;
}

long etl_string::from_base64( unsigned char** dest )
{
    if( dest )
    {
        *dest = (unsigned char*)malloc( length() + 1 );

        unsigned char* p = *dest;
        int            k = 0;
        int            src_len = length() + 1;

        unsigned char* buf = (unsigned char*)malloc( src_len );

        for( k = 0, src_len = 0; c_str()[ k ]; k++ )
        {
            if( (c_str()[ k ] >= 'A' && c_str()[ k ] <= 'Z') ||
                (c_str()[ k ] >= 'a' && c_str()[ k ] <= 'z') ||
                (c_str()[ k ] >= '0' && c_str()[ k ] <= '9') ||
                (c_str()[ k ] == '+')                    ||
                (c_str()[ k ] == '/')                    ||
                (c_str()[ k ] == '=') )
            {
                buf[ src_len++ ] = c_str()[ k ];
            }
        }

        for( k = 0;  k < src_len;  k += 4 )
        {
            char c1 = 'A', c2 = 'A', c3 = 'A', c4 = 'A';
            unsigned char b1 = 0, b2 = 0, b3 = 0, b4 = 0;

            c1 = buf[ k ];

            if( k + 1 < src_len )
                c2 = buf[ k + 1 ];

            if( k + 2 < src_len )
                c3 = buf[ k + 2 ];

            if( k + 3 < src_len )
                c4 = buf[ k + 3 ];

            b1 = decode_char( c1 );
            b2 = decode_char( c2 );
            b3 = decode_char( c3 );
            b4 = decode_char( c4 );

            *p++ = ( (b1<<2) | (b2>>4) );

            if( c3 != '=' )
                *p++ = ( ((b2&0xf)<<4) | (b3>>2) );

            if( c4 != '=' )
                *p++= ( ((b3&0x3)<<6) | b4 );
        }

        free( buf );

        return( p - *dest );
    }

    return -1;
}

int etl_string::match( char* regexp )
{
    const char* text = internalString.c_str();

    if( regexp[0] == '^' )
        return match_here( regexp + 1, text );

    do
    {
        if( match_here( regexp, text ) )
            return 1;
    } while( *text++ != '\0' );

    return 0;
}

int etl_string::find( etl_string subString, int pos )
{
    return internalString.find( subString.internalString, pos );
}

int etl_string::rfind( etl_string subString, int pos )
{
    return internalString.rfind( subString.internalString, pos );
}

etl_string& etl_string::copy(const char* new_string)
{
    internalString = new_string;
    return *this;
}

char etl_string::encode_byte( unsigned char u )
{
    if( u < 26 )
        return 'A' + u;
    if( u < 52 )
        return 'a' + (u - 26);
    if( u < 62 )
        return '0' + (u - 52);
    if( u == 62 )
        return '+';
    return '/';
}

unsigned char etl_string::decode_char( char c )
{
    if( c >= 'A' && c <= 'Z' )
        return( c - 'A' );
    if( c >= 'a' && c <= 'z' )
        return( c - 'a' + 26 );
    if( c >= '0' && c <= '9' )
        return( c - '0' + 52 );
    if( c == '+' )
        return 62;
    return 63;
}

int etl_string::match_here( char* regexp, const char* text )
{
    if( regexp[0] == '\0' )
        return 1;

    if( regexp[1] == '*' )
        return match_star( regexp[0], regexp+2, text );

    if( regexp[0] == '$' && regexp[1] == '\0' )
        return *text == '\0';

    if( *text != '\0' && (regexp[0] == '.' || regexp[0] == *text ) )
        return match_here( regexp+1, text+1 );

    return 0;
}

int etl_string::match_star( int c, char* regexp, const char* text )
{
    do
    {
        if( match_here( regexp, text ) )
            return 1;
    } while( *text != '\0' && (*text++ == c || c == '.') );

    return 0;
}
